/*
 * Copyright  2017  zengp
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package  com.sat.common.widget

import android.graphics.PointF
import android.graphics.Rect
import android.util.DisplayMetrics
import android.util.SparseArray
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView

/**
 * Created by zengp on 2017/11/22.
 */
class WheelPickerLayoutManager(private val mRecyclerView: RecyclerWheelPicker) :
    LinearLayoutManager(mRecyclerView.context) {
    var mVerticalOffset = 0 // offset in vertical orientation when scrolling
    var mItemHeight = 0
    private var mMaxOverScrollOffset = 0
    var mIsOverScroll = false // whether no item cross the center
    private var mItemAreas // record all visible child display area
            : SparseArray<Rect>? = null
    private val MILLISECONDS_PER_INCH = 50f // scroll speed
    override fun setOrientation(orientation: Int) {
        super.setOrientation(VERTICAL)
    }

    override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
        return RecyclerView.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        )
    }

    override fun onLayoutChildren(
        recycler: RecyclerView.Recycler,
        state: RecyclerView.State
    ) {
        super.onLayoutChildren(recycler, state)
        if (itemCount <= 0 || state.isPreLayout) return

        // layout algorithm:
        // 1.scrap all attached views
        // 2. record all visible items display area
        // 3. fill views
        detachAndScrapAttachedViews(recycler)
        val first = recycler.getViewForPosition(0)
        measureChildWithMargins(first, 0, 0)
        val childWidth = getDecoratedMeasuredWidth(first)
        val childHeight = getDecoratedMeasuredHeight(first)
        mItemHeight = childHeight
        mMaxOverScrollOffset = verticalSpace / 2 + childHeight / 2

        // record all the visible items rect
        mItemAreas = SparseArray()
        // first item layout in center
        val topToCenterOffset = verticalSpace / 2 - childHeight / 2
        var offsetHeight = paddingTop + topToCenterOffset
        for (i in 0 until itemCount) {
            val rect = Rect(
                paddingLeft,
                offsetHeight,
                paddingLeft + childWidth,
                offsetHeight + childHeight
            )
            mItemAreas!!.put(i, rect)
            offsetHeight += childHeight
        }

        // fill views
        fillView(recycler, state)
    }

    private fun fillView(
        recycler: RecyclerView.Recycler,
        state: RecyclerView.State
    ) {
        if (itemCount <= 0 || state.isPreLayout) return

        // the visible area for the RecyclerView
        val displayArea = Rect(
            0, mVerticalOffset, horizontalSpace,
            verticalSpace + mVerticalOffset
        )

        // remove invisible child
        val rect = Rect()
        for (i in 0 until childCount) {
            val item = getChildAt(i)
            rect[getDecoratedLeft(item!!), getDecoratedTop(item), getDecoratedRight(item)] =
                getDecoratedBottom(
                    item
                )
            if (!Rect.intersects(displayArea, rect)) {
                removeAndRecycleView(item, recycler)
            }
        }

        // add visible child
        for (i in 0 until itemCount) {
            val area = mItemAreas!![i]
            if (Rect.intersects(displayArea, area)) {
                val child = recycler.getViewForPosition(i)
                addView(child)
                measureChildWithMargins(child, 0, 0)
                val childRect = Rect()
                calculateItemDecorationsForChild(child, childRect)
                layoutDecorated(
                    child, area.left, area.top - mVerticalOffset,
                    area.right, area.bottom - mVerticalOffset
                )
            }
        }
    }

    override fun canScrollVertically(): Boolean {
        return mRecyclerView.isScrollEnabled
    }

    override fun scrollVerticallyBy(
        dy: Int,
        recycler: RecyclerView.Recycler,
        state: RecyclerView.State
    ): Int {
        // scrap all attached views and re-layout by scrolling distance
        var dy = dy
        detachAndScrapAttachedViews(recycler)
        mIsOverScroll = false
        // no items, vertical offset must be 0;
        if (itemCount == 0) {
            dy = 0
            mVerticalOffset = 0
        }
        // scroll to top bound ; dy < 0; mVerticalOffset < 0
        if (dy < 0) {
            if (mVerticalOffset + dy <= -mItemHeight) mIsOverScroll = true
            if (mVerticalOffset + dy <= -mMaxOverScrollOffset) {
                dy = 5
                val offset = -(mVerticalOffset + mMaxOverScrollOffset)
                if (childCount == 0) dy += offset
            }
        }
        // scroll to bottom bound ; dy > 0; mVerticalOffset > 0
        if (dy > 0) {
            val verticalOffset = mVerticalOffset - (itemCount - 1) * mItemHeight
            if (verticalOffset + dy >= 0) mIsOverScroll = true
            if (dy > 0 && verticalOffset + dy >= mMaxOverScrollOffset) {
                dy = -5
                val offset = -(verticalOffset - mMaxOverScrollOffset)
                if (childCount == 0) dy += offset
            }
        }
        offsetChildrenVertical(-dy)
        fillView(recycler, state)
        mVerticalOffset += dy
        return dy
    }

    fun checkVerticalOffsetBound() {
        val totalItemCount = itemCount
        if (totalItemCount == 0) mVerticalOffset =
            0 else if (mVerticalOffset > (totalItemCount - 1) * mItemHeight) {
            mVerticalOffset = (totalItemCount - 1) * mItemHeight
        } else if (mVerticalOffset < 0) mVerticalOffset = 0
    }

    fun findCenterItemPosition(): Int {
        if (itemCount == 0) return 0
        if (mRecyclerView.childCount == 0) return RecyclerView.NO_POSITION
        val first = findFirstCompletelyVisibleItemPosition()
        val last = findLastCompletelyVisibleItemPosition()
        if (first == last) return first
        for (i in first..last) {
            val holder = mRecyclerView.findViewHolderForAdapterPosition(i) ?: continue
            val child = holder.itemView ?: continue
            val centerY = verticalSpace / 2
            val childCenterY = child.top + child.height / 2
            if (Math.abs(centerY - childCenterY) <= 1) return i
        }
        return RecyclerView.NO_POSITION
    }

    private val verticalSpace: Int
        private get() = height - paddingBottom - paddingTop

    private val horizontalSpace: Int
        private get() = width - paddingLeft - paddingRight

    fun scrollTargetPositionToCenter(position: Int, itemHeight: Int) {
        if (position < 0) return
        val distance = itemHeight * position
        //        mRecyclerView.smoothScrollBy(0, distance);
        mRecyclerView.scrollBy(0, distance)
    }

    override fun smoothScrollToPosition(
        recyclerView: RecyclerView,
        state: RecyclerView.State,
        position: Int
    ) {
        val smoothScroller: LinearSmoothScroller =
            object : LinearSmoothScroller(recyclerView.context) {
                override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float {
                    return MILLISECONDS_PER_INCH / displayMetrics.densityDpi
                }

                override fun computeScrollVectorForPosition(targetPosition: Int): PointF? {
                    return this@WheelPickerLayoutManager.computeScrollVectorForPosition(
                        targetPosition
                    )
                }
            }
        smoothScroller.targetPosition = position
        startSmoothScroll(smoothScroller)
    }

    init {
        orientation = VERTICAL
    }
}